package com.zxkj.utils;
import com.zxkj.Exception.BuildParamException;
import com.zxkj.Exception.HrgException;
import com.zxkj.support.Result;
import com.zxkj.support.ResultCode;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.HibernateValidator;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.DataBinder;
import org.springframework.validation.SmartValidator;
import org.springframework.validation.beanvalidation.SpringValidatorAdapter;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 配合Hibernate-Validator注解使用
 *
 * @Description:
 * @Author: hujian
 * @Date: 2020/3/31 16:36
 */
@Slf4j
public class BeanValidationUtils {

    /**
     * 使用hibernate的注解来进行验证
     */
    private static Validator validator;

    /**
     * spring 的校验方式
     */
    private static SmartValidator smartValidator;

    static {
        // 快速失败模式
        validator = Validation
                .byProvider(HibernateValidator.class)
                .configure()
                .failFast(true)
                .buildValidatorFactory()
                .getValidator();
    }

    private static SmartValidator getValidatorAdapter(Validator validator) {
        if (smartValidator == null) {
            smartValidator = new SpringValidatorAdapter(validator);
        }
        return smartValidator;
    }

    /**
     * 对象校验
     *
     * @param obj : 待校验对象
     * @throws HrgException
     * @description:
     * @author yongwu3
     * @date 2020/3/31 16:52
     */
    public static <T> void validate(T obj) {
        // 校验
        Set<ConstraintViolation<T>> constraintViolations = validator.validate(obj);
        // 抛出检验异常
        if (constraintViolations.size() > 0) {
            throw new HrgException(ResultCode.PARAMS_INVALID, constraintViolations.iterator().next().getMessage());
        }
    }

    /**
     * 对象校验
     *
     * @param object :  待校验对象
     * @param groups :  待校验的组
     * @throws HrgException
     * @description: 带分组的校验
     * @author yongwu3
     * @date 2020/3/31 17:55
     */
    public static <T> void validate(T object, Class<?>... groups) {
        Set<ConstraintViolation<T>> constraintViolationSet = validator.validate(object, groups);
        if (!constraintViolationSet.isEmpty()) {
            throw new HrgException(ResultCode.PARAMS_INVALID, constraintViolationSet.iterator().next().getMessage());
        }
    }

    /**
     * @param obj                待校验对象
     * @param specicalProperties 指定校验的字段 如校验用户和密码是否为空 new String[]{"user","password"}
     * @return 返回参数说明:无
     * @description: 校验指定字段
     * @author jianhu9
     * @date 2020/4/6 9:49
     */
    public static<T> void validate(T obj, String... specicalProperties) {
        Map<String,String> map=new HashMap<>();
        for (String str : specicalProperties) {
            // 校验
            Set<ConstraintViolation<T>> constraintViolations = validator.validateProperty(obj, str);
            if (constraintViolations.size() > 0) {
                for (ConstraintViolation<T> constraintViolation : constraintViolations) {
                    map.put(str,constraintViolation.getMessageTemplate());
                }
            }
        }
        if(!map.isEmpty()){
            Result result = new Result();
            result.setCode(ResultCode.PARAMS_INVALID);
            result.setData(map);
            result.setMessage("参数校验异常");
            throw new BuildParamException(result);
        }

    }


    /**
     * 集合校验
     *
     * @param collection : 待校验的集合
     * @throws HrgException
     * @description:
     * @author yongwu3
     * @date 2020/3/31 17:56
     */
    public static void validateList(Collection<?> collection) {
        if(CollectionUtils.isEmpty(collection)){
            throw new NullPointerException(collection+"is null");
        };
        for (Object o : collection) {
            validate(o);
        }
    }

    /**
     * 多个对象同时校验
     *
     * @param first   : 待校验对象
     * @param objects : 待校验对象
     * @return 返回参数说明: 无
     * @description:
     * @author yongwu3
     * @date 2020/3/31 17:57
     */
    public static void validateObjects(Object first, Object... objects) {
        if (objects != null && objects.length > 0) {
            validateList(Arrays.asList(first, objects));
        } else {
            validate(first);
        }
    }


    /**
     * 校验对象
     * 使用与 Spring 集成的校验方式。
     *
     * @param object 待校验对象
     * @param groups 待校验的组
     * @throws HrgException
     */
    public static <T> void validateBySpring(T object, Class<?>... groups) {
        DataBinder dataBinder = getBinder(object);
        dataBinder.validate((Object[]) groups);

        BindingResult bindingResult = dataBinder.getBindingResult();
        if (bindingResult.hasErrors()) {
            log.debug("错误信息：");
            throw new HrgException(ResultCode.PARAMS_INVALID, bindingResult.getFieldErrors().get(0).getDefaultMessage());
        }
    }

    private static <T> DataBinder getBinder(T object) {
        DataBinder dataBinder = new DataBinder(object, ClassUtils.getShortName(object.getClass()));
        dataBinder.setValidator(getValidatorAdapter(validator));
        return dataBinder;
    }

    /**
     * @param obj
     * @return 返回参数说明: 无
     * @description: 通用属性校验
     * @author cwwang4
     * @date 2020/4/13 16:59
     */
    public static void validateObject(Object obj) {
        // 判断是否是Collection
        if (obj instanceof Collection) {
            Collection collection = (Collection) obj;
            if(CollectionUtils.isEmpty(collection)){
                throw new NullPointerException();
            };
            for (Object ob : collection) {
                validateObject(ob);
            }
        }

        //对象字段中含有集合的处理
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            Object fv = getFieldValueByName(field.getName(), obj);
            if (fv instanceof Collection) {
                validateObject(fv);
            }
        }
        // 验证某个对象,其实也可以只验证其中的某一个属性的
        Set<ConstraintViolation<Object>> constraintViolations = validator.validate(obj);
        // 抛出检验异常
        if (constraintViolations.size() > 0) {
            throw new HrgException(ResultCode.PARAMS_INVALID, constraintViolations.iterator().next().getMessage());
        }
    }

    /**
     * @param obj
     * @param specicalProperties
     * @return 返回参数说明: 无
     * @description: 校验指定字段属性
     * @author cwwang4
     * @date 2020/4/13 18:04
     */
    public static <T> void validateSpecicalProperties(T obj, String... specicalProperties) {
        if (obj instanceof Collection) {
            Collection collection = (Collection) obj;
            if(CollectionUtils.isEmpty(collection)){
                throw new NullPointerException();
            };
            for (Object ob : collection) {
                validate(ob, specicalProperties);
            }
        }

        validate(obj, specicalProperties);
    }

    /**
     * @param fieldName
     * @param o
     * @return 返回参数说明: 无
     * @description: 根据属性名获取属性值
     * @author cwwang4
     * @date 2020/4/13 16:59
     */
    public static Object getFieldValueByName(String fieldName, Object o) {
        try {
            return getObject(fieldName, o);
        } catch (NoSuchMethodException | InvocationTargetException e) {
            return null;
        } //忽略方法内部错误
        catch (Exception e) {
            throw new HrgException(ResultCode.PARAMS_INVALID, e.getMessage());
        }
    }

    /**
     * @param fieldName
     * @param o
     * @return 返回参数说明: 无
     * @description: 根据属性名获取属性值
     * @author cwwang4
     * @date 2020/4/13 18:08
     */
    private static Object getObject(String fieldName, Object o) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        String firstLetter = fieldName.substring(0, 1).toUpperCase();
        String getter = "get" + firstLetter + fieldName.substring(1);
        Method method = o.getClass().getMethod(getter, new Class[]{});
        Object value = method.invoke(o, new Object[]{});
        return value;
    }

}
